﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using System.Xml.Serialization;

namespace XingZhe.Helpers
{
        public static class XmlSerializerHelper
        {
                public static string Serialize(object o)
                {
                        if (o == null)
                        {
                                return string.Empty;
                        }

                        XElement xElement = XmlSerializerHelper.Serialize(o, string.Empty);
                        if (xElement == null)
                        {
                                return string.Empty;
                        }

                        MemoryStream stream = new MemoryStream();
                        xElement.Save(stream);
                        stream.Position = 0;

                        byte[] bytes = new byte[stream.Length];
                        stream.Read(bytes, 0, bytes.Length);
                        return System.Text.Encoding.UTF8.GetString(bytes, 0, bytes.Length);
                }

                private static XElement Serialize(object o, string rootName)
                {
                        if (o == null)
                        {
                                return null;
                        }

                        Type oType = o.GetType();
                        if (string.IsNullOrWhiteSpace(rootName))
                        {
                                XmlRootAttribute attribute = XmlSerializerHelper.GetAttribute<XmlRootAttribute>(oType);
                                if (attribute != null)
                                {
                                        rootName = attribute.ElementName;
                                }
                        }

                        if (string.IsNullOrWhiteSpace(rootName))
                        {
                                return null;
                        }

                        XElement rootElement = new XElement(XName.Get(rootName));

                        PropertyInfo[] properties = oType.GetProperties();
                        foreach (var property in properties)
                        {
                                XmlElementAttribute xmlElementAttribute = XmlSerializerHelper.GetAttribute<XmlElementAttribute>(property);

                                if (XmlSerializerHelper.IsXmlTextNode(property.PropertyType))
                                {
                                        XmlAttributeAttribute xmlAttributeAttribute = XmlSerializerHelper.GetAttribute<XmlAttributeAttribute>(property);
                                        if (xmlAttributeAttribute != null)
                                        {
                                                XAttribute att = new XAttribute(XName.Get(xmlAttributeAttribute.AttributeName), property.GetValue(o));
                                                rootElement.Add(att);
                                        }
                                        else if (xmlElementAttribute != null)
                                        {
                                                object value = property.GetValue(o);

                                                if (value != null)
                                                {
                                                        string valueString = string.Format("{0}", value);

                                                        if (value.GetType() == typeof(DateTime) || value.GetType() == typeof(Nullable<DateTime>))
                                                        {
                                                                valueString = string.Format("{0:s}", value);
                                                        }

                                                        if (!string.IsNullOrWhiteSpace(valueString))
                                                        {
                                                                XElement xElement = new XElement(XName.Get(xmlElementAttribute.ElementName));
                                                                xElement.Value = valueString;
                                                                rootElement.Add(xElement);
                                                        }
                                                }
                                        }
                                }
                                else if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType))
                                {
                                        string subRootName = string.Empty;
                                        if (xmlElementAttribute != null)
                                        {
                                                subRootName = xmlElementAttribute.ElementName;
                                        }

                                        object value = property.GetValue(o);
                                        if (value != null)
                                        {
                                                foreach (var item in (IEnumerable)value)
                                                {
                                                        XElement xElement = XmlSerializerHelper.Serialize(item, subRootName);
                                                        if (xElement != null)
                                                        {
                                                                rootElement.Add(xElement);
                                                        }
                                                }
                                        }
                                }
                                else
                                {
                                        string subRootName = string.Empty;
                                        if (xmlElementAttribute != null)
                                        {
                                                subRootName = xmlElementAttribute.ElementName;
                                        }

                                        object value = property.GetValue(o);
                                        if (value != null)
                                        {
                                                XElement xElement = XmlSerializerHelper.Serialize(value, subRootName);
                                                if (xElement != null)
                                                {
                                                        rootElement.Add(xElement);
                                                }
                                        }
                                }
                        }

                        return rootElement;
                }

                private static bool IsXmlTextNode(Type type)
                {
                        return type.IsValueType || type == typeof(Uri) || type == typeof(DateTime) || type == typeof(String);
                }

                private static T GetAttribute<T>(Type type)
                        where T : Attribute
                {
                        if (type == null)
                        {
                                return default(T);
                        }

                        Type targetAttributeType = typeof(T);

                        foreach (var item in type.GetCustomAttributes(true))
                        {
                                if (item.GetType() == targetAttributeType)
                                {
                                        return item as T;
                                }
                        }

                        return default(T);
                }

                private static T GetAttribute<T>(PropertyInfo propertyInfo)
                        where T : Attribute
                {
                        if (propertyInfo == null)
                        {
                                return default(T);
                        }

                        Type targetAttributeType = typeof(T);

                        foreach (object item in propertyInfo.GetCustomAttributes())
                        {
                                if (item.GetType() == targetAttributeType)
                                {
                                        return item as T;
                                }
                        }

                        return default(T);
                }

                private static bool IsNullableType(Type theType)
                {
                        return (theType.IsGenericType
                                && theType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)));
                }
        }
}
